home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 15 / CU Amiga Magazine's Super CD-ROM 15 (1997)(EMAP Images)(GB)[!][issue 1997-10].iso / CUCD / Graphics / Ghostscript / source / dwtext.cpp < prev    next >
C/C++ Source or Header  |  1997-05-09  |  22KB  |  965 lines

  1. /* Copyright (C) 1996, Russell Lang.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19.  
  20. // dwtext.cpp
  21.  
  22.  
  23. // Microsoft Windows 3.n text window for Ghostscript.
  24.  
  25. #include <stdlib.h>
  26. #include <string.h>     // use only far items
  27. #include <ctype.h>
  28.  
  29. #define STRICT
  30. #include <windows.h>
  31. #include <windowsx.h>
  32. #include <commdlg.h>
  33. #include <shellapi.h>
  34.  
  35. #include "dwtext.h"
  36.  
  37. #if defined(_MSC_VER) && defined(__WIN32__)
  38. #define _export
  39. #else
  40. #define max(a,b) ( (a>b) ? a : b )
  41. #define min(a,b) ( (a<b) ? a : b )
  42. #endif
  43.  
  44. #ifndef EOF
  45. #define EOF (-1)
  46. #endif
  47.  
  48. /* sysmenu */
  49. #define M_COPY_CLIP 1
  50.  
  51. LRESULT CALLBACK _export WndTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  52. static const char* TextWinClassName = "rjlTextWinClass";
  53. static const POINT TextWinMinSize = {16, 4};
  54.  
  55. void
  56. TextWindow::error(char *message)
  57. {
  58.     MessageBox((HWND)NULL,message,(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
  59. }
  60.  
  61. /* Bring Cursor into text window */
  62. void
  63. TextWindow::to_cursor(void)
  64. {
  65. int nXinc=0;
  66. int nYinc=0;
  67. int cxCursor;
  68. int cyCursor;
  69.     cyCursor = CursorPos.y * CharSize.y;
  70.     if ( (cyCursor + CharSize.y > ScrollPos.y + ClientSize.y) 
  71. //      || (cyCursor < ScrollPos.y) ) {
  72. // change to scroll to end of window instead of just making visible
  73. // so that ALL of error message can be seen
  74.       || (cyCursor < ScrollPos.y+ClientSize.y) ) {
  75.         nYinc = max(0, cyCursor + CharSize.y - ClientSize.y) - ScrollPos.y;
  76.         nYinc = min(nYinc, ScrollMax.y - ScrollPos.y);
  77.     }
  78.     cxCursor = CursorPos.x * CharSize.x;
  79.     if ( (cxCursor + CharSize.x > ScrollPos.x + ClientSize.x)
  80.       || (cxCursor < ScrollPos.x) ) {
  81.         nXinc = max(0, cxCursor + CharSize.x - ClientSize.x/2) - ScrollPos.x;
  82.         nXinc = min(nXinc, ScrollMax.x - ScrollPos.x);
  83.     }
  84.     if (nYinc || nXinc) {
  85.         ScrollPos.y += nYinc;
  86.         ScrollPos.x += nXinc;
  87.         ScrollWindow(hwnd,-nXinc,-nYinc,NULL,NULL);
  88.         SetScrollPos(hwnd,SB_VERT,ScrollPos.y,TRUE);
  89.         SetScrollPos(hwnd,SB_HORZ,ScrollPos.x,TRUE);
  90.         UpdateWindow(hwnd);
  91.     }
  92. }
  93.  
  94. void
  95. TextWindow::new_line(void)
  96. {
  97.     CursorPos.x = 0;
  98.     CursorPos.y++;
  99.     if (CursorPos.y >= ScreenSize.y) {
  100.         int i =  ScreenSize.x * (ScreenSize.y - 1);
  101.         _fmemmove(ScreenBuffer, ScreenBuffer+ScreenSize.x, i);
  102.         _fmemset(ScreenBuffer + i, ' ', ScreenSize.x);
  103.         CursorPos.y--;
  104.         ScrollWindow(hwnd,0,-CharSize.y,NULL,NULL);
  105.         UpdateWindow(hwnd);
  106.     }
  107.     if (CursorFlag)
  108.         to_cursor();
  109. //    TextMessage();
  110. }
  111.  
  112. /* Update count characters in window at cursor position */
  113. /* Updates cursor position */
  114. void
  115. TextWindow::update_text(int count)
  116. {
  117. HDC hdc;
  118. int xpos, ypos;
  119.     xpos = CursorPos.x*CharSize.x - ScrollPos.x;
  120.     ypos = CursorPos.y*CharSize.y - ScrollPos.y;
  121.     hdc = GetDC(hwnd);
  122.     SelectFont(hdc, hfont);
  123.     TextOut(hdc,xpos,ypos,
  124.         (LPSTR)(ScreenBuffer + CursorPos.y*ScreenSize.x + CursorPos.x),
  125.         count);
  126.     (void)ReleaseDC(hwnd,hdc);
  127.     CursorPos.x += count;
  128.     if (CursorPos.x >= ScreenSize.x)
  129.         new_line();
  130. }
  131.  
  132.  
  133. void
  134. TextWindow::size(int width, int height)
  135. {
  136.     ScreenSize.x =  max(width, TextWinMinSize.x);
  137.     ScreenSize.y =  max(height, TextWinMinSize.y);
  138. }
  139.  
  140. void
  141. TextWindow::font(const char *name, int size)
  142. {
  143.     // reject inappropriate arguments
  144.     if (name == NULL)
  145.     return;
  146.     if (size < 4)
  147.     return;
  148.  
  149.     // set new name and size
  150.     fontname = new char[strlen(name)+1];
  151.     if (fontname == NULL)
  152.     return;
  153.     strcpy(fontname, name);
  154.     fontsize = size;
  155.  
  156.     // make a new font
  157.     LOGFONT lf;
  158.     TEXTMETRIC tm;
  159.     LPSTR p;
  160.     HDC hdc;
  161.     
  162.     // if window not open, hwnd == 0 == HWND_DESKTOP
  163.     hdc = GetDC(hwnd);
  164.     _fmemset(&lf, 0, sizeof(LOGFONT));
  165.     _fstrncpy(lf.lfFaceName,fontname,LF_FACESIZE);
  166.     lf.lfHeight = -MulDiv(fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
  167.     lf.lfPitchAndFamily = FIXED_PITCH;
  168.     lf.lfCharSet = DEFAULT_CHARSET;
  169.     if ( (p = _fstrstr(fontname," Italic")) != (LPSTR)NULL ) {
  170.     lf.lfFaceName[ (unsigned int)(p-fontname) ] = '\0';
  171.     lf.lfItalic = TRUE;
  172.     }
  173.     if ( (p = _fstrstr(fontname," Bold")) != (LPSTR)NULL ) {
  174.     lf.lfFaceName[ (unsigned int)(p-fontname) ] = '\0';
  175.     lf.lfWeight = FW_BOLD;
  176.     }
  177.     if (hfont)
  178.     DeleteFont(hfont);
  179.  
  180.     hfont = CreateFontIndirect((LOGFONT FAR *)&lf);
  181.  
  182.     /* get text size */
  183.     SelectFont(hdc, hfont);
  184.     GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
  185.     CharSize.y = tm.tmHeight;
  186.     CharSize.x = tm.tmAveCharWidth;
  187.     CharAscent = tm.tmAscent;
  188.     if (bFocus)
  189.     CreateCaret(hwnd, 0, CharSize.x, 2+CaretHeight);
  190.     ReleaseDC(hwnd, hdc);
  191.  
  192.     // redraw window if necessary
  193.     if (hwnd != HWND_DESKTOP) {
  194.     // INCOMPLETE
  195.     }
  196. }
  197.  
  198.  
  199.  
  200. // Set drag strings
  201. void
  202. TextWindow::drag(const char *pre, const char *post)
  203. {
  204.     // remove old strings
  205.     delete DragPre;
  206.     DragPre = NULL;
  207.     delete DragPost;
  208.     DragPost = NULL;
  209.  
  210.     // add new strings
  211.     DragPre = new char[strlen(pre)+1];
  212.     if (DragPre)
  213.     strcpy(DragPre, pre);
  214.     DragPost = new char[strlen(post)+1];
  215.     if (DragPost)
  216.     strcpy(DragPost, post);
  217. }
  218.  
  219. // Text Window constructor
  220. TextWindow::TextWindow(void)
  221. {
  222.     // make sure everything is null
  223.     memset(this, 0, sizeof(TextWindow));
  224.  
  225.     // set some defaults
  226.     font("Courier New", 10);
  227.     size(80, 24);
  228.     KeyBufSize = 2048;
  229.     CursorFlag = 1;    // scroll to cursor after \n or \r
  230. }
  231.  
  232. // TextWindow destructor
  233. TextWindow::~TextWindow(void)
  234. {
  235.     if (hwnd)
  236.         DestroyWindow(hwnd);
  237.     if (hfont)
  238.     DeleteFont(hfont);
  239.  
  240.     delete KeyBuf;
  241.     delete ScreenBuffer;
  242.     delete DragPre;
  243.     delete DragPost;
  244. }
  245.  
  246. // register the window class
  247. int
  248. TextWindow::register_class(HINSTANCE hinst, HICON hicon)
  249. {
  250.     hInstance = hinst;
  251.     hIcon = hicon;
  252.  
  253.     // register window class
  254.     WNDCLASS wndclass;
  255.     wndclass.style = CS_HREDRAW | CS_VREDRAW;
  256.     wndclass.lpfnWndProc = WndTextProc;
  257.     wndclass.cbClsExtra = 0;
  258.     wndclass.cbWndExtra = sizeof(void FAR *);
  259.     wndclass.hInstance = hInstance;
  260.     wndclass.hIcon = hIcon ? hIcon : LoadIcon(NULL, IDI_APPLICATION);
  261.     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  262.     wndclass.hbrBackground = GetStockBrush(WHITE_BRUSH);
  263.     wndclass.lpszMenuName = NULL;
  264.     wndclass.lpszClassName = TextWinClassName;
  265.     return RegisterClass(&wndclass);
  266. }
  267.  
  268. // Show the window
  269. int
  270. TextWindow::create(LPSTR app_name, int show_cmd)
  271. {
  272.     HMENU sysmenu;
  273.  
  274.     Title = app_name;
  275.     nCmdShow = show_cmd;
  276.     quitnow = FALSE;
  277.  
  278.     // make sure we have some sensible defaults
  279.     if (KeyBufSize < 256)
  280.     KeyBufSize = 256;
  281.  
  282.     CursorPos.x = CursorPos.y = 0;
  283.     bFocus = FALSE;
  284.     bGetCh = FALSE;
  285.     CaretHeight = 0;
  286.  
  287.  
  288.     // allocate buffers
  289.     KeyBufIn = KeyBufOut = KeyBuf = new BYTE[KeyBufSize];
  290.     if (KeyBuf == NULL) {
  291.     error("Out of memory");
  292.     return 1;
  293.     }
  294.     ScreenBuffer = new BYTE[ScreenSize.x * ScreenSize.y];
  295.     if (ScreenBuffer == NULL) {
  296.     error("Out of memory");
  297.     return 1;
  298.     }
  299.     _fmemset(ScreenBuffer, ' ', ScreenSize.x * ScreenSize.y);
  300.  
  301.     hwnd = CreateWindow(TextWinClassName, Title,
  302.           WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
  303.           CW_USEDEFAULT, CW_USEDEFAULT,
  304.           CW_USEDEFAULT, CW_USEDEFAULT,
  305.           NULL, NULL, hInstance, this);
  306.  
  307.     if (hwnd == NULL) {
  308.     MessageBox((HWND)NULL,"Couldn't open text window",(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
  309.     return 1;
  310.     }
  311.  
  312.     ShowWindow(hwnd, nCmdShow);
  313.     sysmenu = GetSystemMenu(hwnd,0);    /* get the sysmenu */
  314.     AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
  315.     AppendMenu(sysmenu, MF_STRING, M_COPY_CLIP, "Copy to Clip&board");
  316.  
  317.     return 0;
  318. }
  319.  
  320. int
  321. TextWindow::destroy(void)
  322. {
  323.     if (hwnd) {
  324.         DestroyWindow(hwnd);
  325.     hwnd = (HWND)NULL;
  326.     }
  327.     return 0;
  328. }
  329.  
  330.  
  331.  
  332. int
  333. TextWindow::putch(int ch)
  334. {
  335. int pos;
  336.     switch(ch) {
  337.     case '\r':
  338.         CursorPos.x = 0;
  339.         if (CursorFlag)
  340.             to_cursor();
  341.         break;
  342.     case '\n':
  343.         new_line();
  344.         break;
  345.     case 7:
  346.         MessageBeep(-1);
  347.         if (CursorFlag)
  348.             to_cursor();
  349.         break;
  350.     case '\t':
  351.         {
  352.             for ( int n = 8 - (CursorPos.x % 8); n>0; n-- )
  353.                 putch(' ');
  354.         }
  355.         break;
  356.     case 0x08:
  357.     case 0x7f:
  358.         CursorPos.x--;
  359.         if (CursorPos.x < 0) {
  360.             CursorPos.x = ScreenSize.x - 1;
  361.             CursorPos.y--;
  362.         }
  363.         if (CursorPos.y < 0)
  364.             CursorPos.y = 0;
  365.         break;
  366.     default:
  367.         pos = CursorPos.y*ScreenSize.x + CursorPos.x;
  368.         ScreenBuffer[pos] = ch;
  369.         update_text(1);
  370.     }
  371.     return ch;
  372. }
  373.  
  374. void 
  375. TextWindow::write_buf(LPSTR str, int cnt)
  376. {
  377. BYTE FAR *p;
  378. int count, limit;
  379.     while (cnt>0) {
  380.     p = ScreenBuffer + CursorPos.y*ScreenSize.x + CursorPos.x;
  381.     limit = ScreenSize.x - CursorPos.x;
  382.     for (count=0; (count < limit) && (cnt>0) && (isprint(*str) || *str=='\t'); count++) {
  383.         if (*str=='\t') {
  384.         for ( int n = 8 - ((CursorPos.x+count) % 8); (count < limit) & (n>0); n--, count++ )
  385.             *p++ = ' ';
  386.         str++;
  387.         count--;
  388.            }
  389.         else {
  390.         *p++ = *str++;
  391.         }
  392.         cnt--;
  393.     }
  394.     if (count>0) {
  395.         update_text(count);
  396.     }
  397.     if (cnt > 0) {
  398.         if (*str=='\n') {
  399.         new_line();
  400.         str++;
  401.         cnt--;
  402.         }
  403.         else if (!isprint(*str) && *str!='\t') {
  404.         putch(*str++);
  405.         cnt--;
  406.         }
  407.     }
  408.     }
  409. }
  410.  
  411. // Put string to window
  412. void
  413. TextWindow::puts(LPSTR str)
  414. {
  415.     write_buf(str, lstrlen(str));
  416. }
  417.  
  418.  
  419. /* TRUE if key hit, FALSE if no key */
  420. inline int
  421. TextWindow::kbhit(void)
  422. {
  423.     return (KeyBufIn != KeyBufOut);
  424. }
  425.  
  426. /* get character from keyboard, no echo */
  427. /* need to add extended codes */
  428. int
  429. TextWindow::getch(void)
  430. {
  431.     int ch;
  432.     to_cursor();
  433.     bGetCh = TRUE;
  434.     if (bFocus) {
  435.     SetCaretPos(CursorPos.x*CharSize.x - ScrollPos.x,
  436.         CursorPos.y*CharSize.y + CharAscent - CaretHeight - ScrollPos.y);
  437.     ShowCaret(hwnd);
  438.     }
  439.  
  440.     MSG msg;
  441.     do {
  442.         if (!quitnow && GetMessage(&msg, (HWND)NULL, 0, 0)) {
  443.         TranslateMessage(&msg);
  444.         DispatchMessage(&msg);
  445.     }
  446.     else
  447.        return EOF;    /* window closed */
  448.     } while (!kbhit());
  449.  
  450.     ch = *KeyBufOut++;
  451.     if (ch=='\r')
  452.     ch = '\n';
  453.     if (KeyBufOut - KeyBuf >= KeyBufSize)
  454.     KeyBufOut = KeyBuf;        // wrap around
  455.     if (bFocus)
  456.     HideCaret(hwnd);
  457.     bGetCh = FALSE;
  458.     return ch;
  459. }
  460.  
  461. // Read line from keyboard
  462. // Return at most 'len' characters
  463. // Does NOT add null terminating character
  464. // This does is NOT the same as fgets
  465. int
  466. TextWindow::read_line(LPSTR line, int len)
  467. {    
  468. LPSTR dest = line;
  469. LPSTR limit = dest + len - 1;  // leave room for '\n'
  470. int ch;
  471.     if (dest > limit)
  472.     return 0;
  473.     while ( (ch = getch()) != '\n' ) {
  474.     switch(ch) {
  475.         case EOF:
  476.         case 26:    // ^Z == EOF
  477.         return 0;
  478.         case '\b':    // ^H
  479.         case 0x7f:  // DEL
  480.         if (dest > line) {
  481.             putch('\b');
  482.             putch(' ');
  483.             putch('\b');
  484.             --dest;
  485.         }
  486.         break;
  487.         case 21:    // ^U
  488.         while (dest > line) {
  489.             putch('\b');
  490.             putch(' ');
  491.             putch('\b');
  492.             --dest;
  493.         }
  494.         break;
  495.         default:
  496.         if (dest == limit)
  497.             MessageBeep(-1);
  498.         else {
  499.             *dest++ = ch;
  500.             putch(ch);
  501.         }
  502.         break;
  503.     }
  504.     }
  505.     *dest++ = ch;
  506.     putch(ch);
  507.     return (dest-line);
  508. }
  509.  
  510. // Read a string from the keyboard, of up to len characters
  511. // (not including trailing NULL)
  512. int
  513. TextWindow::gets(LPSTR line, int len)
  514. {
  515. LPSTR dest = line;
  516. LPSTR limit = dest + len;  // don't leave room for '\0'
  517. int ch;
  518.     do {
  519.     if (dest >= limit)
  520.         break;
  521.     ch = getch();
  522.     switch(ch) {
  523.         case 26:    // ^Z == EOF
  524.         return 0;
  525.         case '\b':    // ^H
  526.         case 0x7f:  // DEL
  527.         if (dest > line) {
  528.             putch('\b');
  529.             putch(' ');
  530.             putch('\b');
  531.             --dest;
  532.         }
  533.         break;
  534.         case 21:    // ^U
  535.         while (dest > line) {
  536.             putch('\b');
  537.             putch(' ');
  538.             putch('\b');
  539.             --dest;
  540.         }
  541.         break;
  542.         default:
  543.         *dest++ = ch;
  544.         putch(ch);
  545.         break;
  546.     }
  547.     } while (ch != '\n');
  548.  
  549.     *dest = '\0';
  550.     return (dest-line);
  551. }
  552.  
  553.  
  554. /* Windows 3.1 drag-drop feature */
  555. void
  556. TextWindow::drag_drop(HDROP hdrop)
  557. {
  558.     int i, cFiles;
  559.     LPSTR p;
  560.     if ( (DragPre==(LPSTR)NULL) || (DragPost==(LPSTR)NULL) )
  561.         return;
  562.     char szFile[256];
  563.  
  564.     cFiles = DragQueryFile(hdrop, 0xffff, (LPSTR)NULL, 0);
  565.     for (i=0; i<cFiles; i++) {
  566.     DragQueryFile(hdrop, i, szFile, 80);
  567.     for (p=DragPre; *p; p++)
  568.         SendMessage(hwnd,WM_CHAR,*p,1L);
  569.     for (p=szFile; *p; p++) {
  570.         if (*p == '\\')
  571.         SendMessage(hwnd,WM_CHAR,'/',1L);
  572.         else 
  573.         SendMessage(hwnd,WM_CHAR,*p,1L);
  574.     }
  575.     for (p=DragPost; *p; p++)
  576.         SendMessage(hwnd,WM_CHAR,*p,1L);
  577.     }
  578.     DragFinish(hdrop);
  579. }
  580.  
  581.  
  582. void
  583. TextWindow::copy_to_clipboard(void)
  584. {
  585.     int size, count;
  586.     HGLOBAL hGMem;
  587.     LPSTR cbuf, cp;
  588.     TEXTMETRIC tm;
  589.     UINT type;
  590.     HDC hdc;
  591.     int i;
  592.  
  593.     size = ScreenSize.y * (ScreenSize.x + 2) + 1;
  594.     hGMem = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)size);
  595.     cbuf = cp = (LPSTR)GlobalLock(hGMem);
  596.     if (cp == (LPSTR)NULL)
  597.     return;
  598.     
  599.     for (i=0; i<ScreenSize.y; i++) {
  600.     count = ScreenSize.x;
  601.     _fmemcpy(cp, ScreenBuffer + ScreenSize.x*i, count);
  602.     /* remove trailing spaces */
  603.     for (count=count-1; count>=0; count--) {
  604.         if (cp[count]!=' ')
  605.             break;
  606.         cp[count] = '\0';
  607.     }
  608.     cp[++count] = '\r';
  609.     cp[++count] = '\n';
  610.     cp[++count] = '\0';
  611.     cp += count;
  612.     }
  613.     size = _fstrlen(cbuf) + 1;
  614.     GlobalUnlock(hGMem);
  615.     hGMem = GlobalReAlloc(hGMem, (DWORD)size, GHND | GMEM_SHARE);
  616.     /* find out what type to put into clipboard */
  617.     hdc = GetDC(hwnd);
  618.     SelectFont(hdc, hfont);
  619.     GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
  620.     if (tm.tmCharSet == OEM_CHARSET)
  621.     type = CF_OEMTEXT;
  622.     else
  623.     type = CF_TEXT;
  624.     ReleaseDC(hwnd, hdc);
  625.     /* give buffer to clipboard */
  626.     OpenClipboard(hwnd);
  627.     EmptyClipboard();
  628.     SetClipboardData(type, hGMem);
  629.     CloseClipboard();
  630. }
  631.  
  632. /* text window */
  633. LRESULT CALLBACK _export
  634. WndTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  635. {
  636.     TextWindow *tw;
  637.     if (message == WM_CREATE) {
  638.     // Object is stored in window extra data.
  639.     // Nothing must try to use the object before WM_CREATE
  640.     // initializes it here.
  641.     tw = (TextWindow *)(((CREATESTRUCT FAR *)lParam)->lpCreateParams);
  642.     SetWindowLong(hwnd, 0, (LONG)tw);
  643.     }
  644.  
  645.     // call the object window procedure
  646.     tw = (TextWindow *)GetWindowLong(hwnd, 0);
  647.     return tw->WndProc(hwnd, message, wParam, lParam);
  648. }
  649.  
  650.  
  651. // member window procedure
  652. LRESULT
  653. TextWindow::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  654. {
  655.     HDC hdc;
  656.     PAINTSTRUCT ps;
  657.     RECT rect;
  658.     int nYinc, nXinc;
  659.  
  660.     switch(message) {
  661.     case WM_SYSCOMMAND:
  662.         switch(LOWORD(wParam)) {
  663.         case M_COPY_CLIP:
  664.             copy_to_clipboard();
  665.             return 0;
  666.         }
  667.         break;
  668.     case WM_SETFOCUS: 
  669.         bFocus = TRUE;
  670.         CreateCaret(hwnd, 0, CharSize.x, 2+CaretHeight);
  671.         SetCaretPos(CursorPos.x*CharSize.x - ScrollPos.x,
  672.             CursorPos.y*CharSize.y + CharAscent
  673.              - CaretHeight - ScrollPos.y);
  674.         if (bGetCh)
  675.             ShowCaret(hwnd);
  676.         break;
  677.     case WM_KILLFOCUS: 
  678.         DestroyCaret();
  679.         bFocus = FALSE;
  680.         break;
  681.     case WM_SIZE:
  682.         ClientSize.y = HIWORD(lParam);
  683.         ClientSize.x = LOWORD(lParam);
  684.  
  685.         ScrollMax.y = max(0, CharSize.y*ScreenSize.y - ClientSize.y);
  686.         ScrollPos.y = min(ScrollPos.y, ScrollMax.y);
  687.  
  688.         SetScrollRange(hwnd, SB_VERT, 0, ScrollMax.y, FALSE);
  689.         SetScrollPos(hwnd, SB_VERT, ScrollPos.y, TRUE);
  690.  
  691.         ScrollMax.x = max(0, CharSize.x*ScreenSize.x - ClientSize.x);
  692.         ScrollPos.x = min(ScrollPos.x, ScrollMax.x);
  693.  
  694.         SetScrollRange(hwnd, SB_HORZ, 0, ScrollMax.x, FALSE);
  695.         SetScrollPos(hwnd, SB_HORZ, ScrollPos.x, TRUE);
  696.  
  697.         if (bFocus && bGetCh) {
  698.         SetCaretPos(CursorPos.x*CharSize.x - ScrollPos.x,
  699.                 CursorPos.y*CharSize.y + CharAscent 
  700.                 - CaretHeight - ScrollPos.y);
  701.         ShowCaret(hwnd);
  702.         }
  703.         return(0);
  704.     case WM_VSCROLL:
  705.         switch(LOWORD(wParam)) {
  706.         case SB_TOP:
  707.             nYinc = -ScrollPos.y;
  708.             break;
  709.         case SB_BOTTOM:
  710.             nYinc = ScrollMax.y - ScrollPos.y;
  711.             break;
  712.         case SB_LINEUP:
  713.             nYinc = -CharSize.y;
  714.             break;
  715.         case SB_LINEDOWN:
  716.             nYinc = CharSize.y;
  717.             break;
  718.         case SB_PAGEUP:
  719.             nYinc = min(-1,-ClientSize.y);
  720.             break;
  721.         case SB_PAGEDOWN:
  722.             nYinc = max(1,ClientSize.y);
  723.             break;
  724.         case SB_THUMBPOSITION:
  725. #ifdef __WIN32__
  726.             nYinc = HIWORD(wParam) - ScrollPos.y;
  727. #else
  728.             nYinc = LOWORD(lParam) - ScrollPos.y;
  729. #endif
  730.             break;
  731.         default:
  732.             nYinc = 0;
  733.         }
  734.         if ( (nYinc = max(-ScrollPos.y, 
  735.             min(nYinc, ScrollMax.y - ScrollPos.y)))
  736.             != 0 ) {
  737.             ScrollPos.y += nYinc;
  738.             ScrollWindow(hwnd,0,-nYinc,NULL,NULL);
  739.             SetScrollPos(hwnd,SB_VERT,ScrollPos.y,TRUE);
  740.             UpdateWindow(hwnd);
  741.         }
  742.         return(0);
  743.     case WM_HSCROLL:
  744.         switch(LOWORD(wParam)) {
  745.         case SB_LINEUP:
  746.             nXinc = -CharSize.x;
  747.             break;
  748.         case SB_LINEDOWN:
  749.             nXinc = CharSize.x;
  750.             break;
  751.         case SB_PAGEUP:
  752.             nXinc = min(-1,-ClientSize.x);
  753.             break;
  754.         case SB_PAGEDOWN:
  755.             nXinc = max(1,ClientSize.x);
  756.             break;
  757.         case SB_THUMBPOSITION:
  758. #ifdef __WIN32__
  759.             nXinc = HIWORD(wParam) - ScrollPos.x;
  760. #else
  761.             nXinc = LOWORD(lParam) - ScrollPos.x;
  762. #endif
  763.             break;
  764.         default:
  765.             nXinc = 0;
  766.         }
  767.         if ( (nXinc = max(-ScrollPos.x, 
  768.             min(nXinc, ScrollMax.x - ScrollPos.x)))
  769.             != 0 ) {
  770.             ScrollPos.x += nXinc;
  771.             ScrollWindow(hwnd,-nXinc,0,NULL,NULL);
  772.             SetScrollPos(hwnd,SB_HORZ,ScrollPos.x,TRUE);
  773.             UpdateWindow(hwnd);
  774.         }
  775.         return(0);
  776.     case WM_KEYDOWN:
  777.         if (GetKeyState(VK_SHIFT) < 0) {
  778.           switch(wParam) {
  779.         case VK_HOME:
  780.             SendMessage(hwnd, WM_VSCROLL, SB_TOP, (LPARAM)0);
  781.             break;
  782.         case VK_END:
  783.             SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, (LPARAM)0);
  784.             break;
  785.         case VK_PRIOR:
  786.             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, (LPARAM)0);
  787.             break;
  788.         case VK_NEXT:
  789.             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, (LPARAM)0);
  790.             break;
  791.         case VK_UP:
  792.             SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, (LPARAM)0);
  793.             break;
  794.         case VK_DOWN:
  795.             SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, (LPARAM)0);
  796.             break;
  797.         case VK_LEFT:
  798.             SendMessage(hwnd, WM_HSCROLL, SB_LINEUP, (LPARAM)0);
  799.             break;
  800.         case VK_RIGHT:
  801.             SendMessage(hwnd, WM_HSCROLL, SB_LINEDOWN, (LPARAM)0);
  802.             break;
  803.           }
  804.         }
  805.         else {
  806.             switch(wParam) {
  807.             case VK_HOME:
  808.             case VK_END:
  809.             case VK_PRIOR:
  810.             case VK_NEXT:
  811.             case VK_UP:
  812.             case VK_DOWN:
  813.             case VK_LEFT:
  814.             case VK_RIGHT:
  815.             case VK_DELETE:
  816.             { /* store key in circular buffer */
  817.             long count = KeyBufIn - KeyBufOut;
  818.             if (count < 0) count += KeyBufSize;
  819.             if (count < KeyBufSize-2) {
  820.                 *KeyBufIn++ = 0;
  821.                 if (KeyBufIn - KeyBuf >= KeyBufSize)
  822.                 KeyBufIn = KeyBuf;    /* wrap around */
  823.                 *KeyBufIn++ = HIWORD(lParam) & 0xff;
  824.                 if (KeyBufIn - KeyBuf >= KeyBufSize)
  825.                 KeyBufIn = KeyBuf;    /* wrap around */
  826.             }
  827.             }
  828.             }
  829.         }
  830.         break;
  831.     case WM_CHAR:
  832.         { /* store key in circular buffer */
  833.         long count = KeyBufIn - KeyBufOut;
  834.         if (count < 0) count += KeyBufSize;
  835.         if (count < KeyBufSize-1) {
  836.             *KeyBufIn++ = wParam;
  837.             if (KeyBufIn - KeyBuf >= KeyBufSize)
  838.             KeyBufIn = KeyBuf;    /* wrap around */
  839.         }
  840.         }
  841.         return(0);
  842.     case WM_PAINT:
  843.         {
  844.         POINT source, width, dest;
  845.         hdc = BeginPaint(hwnd, &ps);
  846.         SelectFont(hdc, hfont);
  847.         SetMapMode(hdc, MM_TEXT);
  848.         SetBkMode(hdc,OPAQUE);
  849.         GetClientRect(hwnd, &rect);
  850.         source.x = (rect.left + ScrollPos.x) / CharSize.x;        /* source */
  851.         source.y = (rect.top + ScrollPos.y) / CharSize.y;
  852.         dest.x = source.x * CharSize.x - ScrollPos.x;                 /* destination */
  853.         dest.y = source.y * CharSize.y - ScrollPos.y;
  854.         width.x = ((rect.right  + ScrollPos.x + CharSize.x - 1) / CharSize.x) - source.x; /* width */
  855.         width.y = ((rect.bottom + ScrollPos.y + CharSize.y - 1) / CharSize.y) - source.y;
  856.         if (source.x < 0)
  857.             source.x = 0;
  858.         if (source.y < 0)
  859.             source.y = 0;
  860.         if (source.x+width.x > ScreenSize.x)
  861.             width.x = ScreenSize.x - source.x;
  862.         if (source.y+width.y > ScreenSize.y)
  863.             width.y = ScreenSize.y - source.y;
  864.         /* for each line */
  865.         while (width.y>0) {
  866.             TextOut(hdc,dest.x,dest.y,
  867.             (LPSTR)(ScreenBuffer + source.y*ScreenSize.x + source.x),
  868.             width.x);
  869.             dest.y += CharSize.y;
  870.             source.y++;
  871.             width.y--;
  872.         }
  873.         EndPaint(hwnd, &ps);
  874.         return 0;
  875.         }
  876.     case WM_DROPFILES:
  877.         drag_drop((HDROP)wParam);
  878.         break;
  879.     case WM_CREATE:
  880.         {
  881.         RECT crect, wrect;
  882.  
  883.         TextWindow::hwnd = hwnd;
  884.  
  885.         GetClientRect(hwnd, &crect);
  886.         if ( (CharSize.y*ScreenSize.y < crect.bottom)
  887.           || (CharSize.x*ScreenSize.x < crect.right) ) {
  888.             /* shrink size */
  889.         GetWindowRect(hwnd,&wrect);
  890.         MoveWindow(hwnd, wrect.left, wrect.top,
  891.              wrect.right-wrect.left + (CharSize.x*ScreenSize.x - crect.right),
  892.              wrect.bottom-wrect.top + (CharSize.y*ScreenSize.y - crect.bottom),
  893.              TRUE);
  894.         }
  895.         if ( (DragPre!=(LPSTR)NULL) && (DragPost!=(LPSTR)NULL) )
  896.         DragAcceptFiles(hwnd, TRUE);
  897.         }
  898.         break;
  899.     case WM_CLOSE:
  900.         break;
  901.     case WM_DESTROY:
  902.         DragAcceptFiles(hwnd, FALSE);
  903.         if (hfont)
  904.         DeleteFont(hfont);
  905.         hfont = (HFONT)0;
  906.         quitnow = TRUE;
  907.         PostQuitMessage(0);
  908.         break;
  909.     }
  910.     return DefWindowProc(hwnd, message, wParam, lParam);
  911. }
  912.  
  913.  
  914.  
  915.  
  916. #ifdef NOTUSED
  917. // test program
  918. #pragma argsused
  919.  
  920. int PASCAL 
  921. WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
  922. {
  923.  
  924. // make a test window
  925. TextWindow* textwin = new TextWindow(hInstance);
  926.     if (!hPrevInstance) {
  927.         HICON hicon = LoadIcon(NULL, IDI_APPLICATION);
  928.         textwin->register_class(hicon);
  929.     }
  930.     textwin->font("Courier New", 10);
  931.     textwin->size(80, 80);
  932.     textwin->drag("(", ") run\r");
  933.  
  934.     // show the text window
  935.     if (!textwin->show("Application Name", nCmdShow)) {
  936.         // do the real work here
  937.         // TESTING
  938.         int ch;
  939.         int len;
  940.         char *line = new char[256];
  941.         while ( (len = textwin->read_line(line, 256-1)) != 0 ) {
  942.         textwin->write_buf(line, len);
  943.         }
  944. /*
  945.         while ( textwin->gets(line, 256-1) ) {
  946.         textwin->puts(line);
  947.         }
  948. */
  949. /*
  950.         while ( (ch = textwin->getch()) != 4 )
  951.         textwin->putch(ch);
  952. */
  953.     }
  954.     else {
  955.         
  956.     }
  957.  
  958.     // clean up
  959.     delete textwin;
  960.     
  961.     // end program
  962.     return 0;
  963. }
  964. #endif
  965.